joinコマンドを使ってシェルでデータを結合する
コマンドの基本形
コマンドの基本形は以下のような感じです。 今回はBSD版のコマンドを利用しています。
$ join fileA fileB
ファイル二つを引数として取り、その結合結果を出力します。 注意点として「使用するファイルの結合に使うキーがソートされている必要があります」
今回使用するデータ
今回はGDPトップ3の国のリストと都市のデータを扱います。
ID,国 0,アメリカ 1,中国 2,日本
ID,都市,国ID 0,ニューヨーク,0 1,ロサンゼルス,0 2,シカゴ,0 3,北京,1 4,東京,2 5,大阪,2 6,パリ,3
ここでは説明のためにヘッダーが入っていますが、以下の説明ではヘッダーを削除したファイルを使用しているので注意してください。
内部結合
内部結合では結合に成功した行のみが出力されます。
$ join -t , -o '0,1.2,2.2' -1 1 -2 3 country.csv city.csv 0,アメリカ,ニューヨーク 0,アメリカ,ロサンゼルス 0,アメリカ,シカゴ 1,中国,北京 2,日本,東京 2,日本,大阪
オプションについて軽く説明します。
-t
: 区切り文字-o
: 出力するカラム-1
: 1つ目のファイルで結合に使用するカラム-2
: 2つ目のファイルで結合に使用するカラム
ここではcountry.csv
の1番目のカラムであるIDとcity.csv
の3番目のカラムである国IDを使用して結合を行っています。
パリは対応する国IDがないので出力されていません。
-o
の書式についても軽く説明します。
0
は特別で結合に使用したキーが入ります。
1.x
は1つ目のファイルのX番目のカラムです。
2.x
は2つ目のファイルのX番目のカラムです。
外部結合
左外部結合では1つ目のファイルの行は結合の結果によらず出力されます
$ join -t , -o '0,1.2,2.2' -a 1 -e NULL -1 1 -2 3 country.csv city.csv 0,アメリカ,ニューヨーク 0,アメリカ,ロサンゼルス 0,アメリカ,シカゴ 1,中国,北京 2,日本,東京 2,日本,大阪
逆に右外部結合の結果を見てみましょう
$ join -t , -o '0,1.2,2.2' -a 2 -e NULL -1 1 -2 3 country.csv city.csv 0,アメリカ,ニューヨーク 0,アメリカ,ロサンゼルス 0,アメリカ,シカゴ 1,中国,北京 2,日本,東京 2,日本,大阪 3,NULL,パリ
こちらはパリの行が出力されています。
つまりはcity.csv
のすべての行が出力されているということです。
ここで新たに出てきたオプションについて説明します。
-a
: 対応するものがなくても出力するファイル-e
: 対応する値がなかった場合に入れる値
ここでは-a
でどちらのファイルの行を全て使うのかを指定しています。
便宜的に一つ目のファイルを元にした場合を左外部結合、2つ目のファイルの時は右外部結合と呼んでいます。
-e
では対応する値がなかった場合に代わりに入れる文字列を指定しています。
ソートしていないとどうなるか
join
は使用するカラムでソートされているという前提があります。
先ほどのcity.csv
の順番をバラバラにして結合してみます。
0,ニューヨーク,0 1,パリ,3 2,北京,1 3,ロサンゼルス,0 4,大阪,2 5,シカゴ,0 6,東京,2
$ join -t , -o '0,1.2,2.2' -1 1 -2 3 country.csv city_not_sorted.csv 0,アメリカ,ニューヨーク
正しい結果が出力されていません。 ニューヨークはたまたま出力されていますが、他の行は全て抜けています。
$ join -t , -o '0,1.2,2.2' -1 1 -2 3 country.csv <(sort -t , -k 3 city_not_sorted.csv) 0,アメリカ,ニューヨーク 0,アメリカ,ロサンゼルス 0,アメリカ,シカゴ 1,中国,北京 2,日本,大阪 2,日本,東京
プロセス置換を利用してファイルをソートしてから利用します。 こちらは期待通りの結果が得られました。
終わりに
join
コマンドを利用するとこで二つのデータを結合することができました。
ソート済みのファイルでないと正しい結果が出ないという点は注意が必要です。